package de.h_da.fbi.cg.pointcloud.run;

import java.awt.Color;
import java.io.IOException;
import java.util.HashMap;

import org.anddev.progressmonitor.ProgressMonitor;

import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;

import de.h_da.fbi.cg.pointcloud.adt.PointCloud;
import de.h_da.fbi.cg.pointcloud.ply.SimplePLYParser;
import de.h_da.fbi.cg.pointcloud.ply.adt.PLYConfigTransformation;
import de.h_da.fbi.cg.pointcloud.ply.conf.SimplePLYConfigParser;
import de.h_da.fbi.cg.pointcloud.ui.dialog.ProgressDialog;

/**
 * @author Nicolas Gramlich
 * @since 15:49:47 - 11.11.2010
 */
public class StanfordBunny extends SimpleApplication {
	// ===========================================================
	// Constants
	// ===========================================================

	// ===========================================================
	// Fields
	// ===========================================================

	private Material mSolidColorMaterial;
	private float mRotation;

	// ===========================================================
	// Constructors
	// ===========================================================

	public static void main(final String[] args) {
		final StanfordBunny app = new StanfordBunny();
		app.start();
	}

	// ===========================================================
	// Getter & Setter
	// ===========================================================

	// ===========================================================
	// Methods for/from SuperClass/Interfaces
	// ===========================================================

	@Override
	public void simpleInitApp() {
		this.mSolidColorMaterial = new Material(StanfordBunny.this.assetManager, "Common/MatDefs/Misc/SolidColor.j3md");

		this.cam.getLocation().y = 0.1f;
		this.cam.getLocation().z = 0.5f;
		this.cam.setFrustumPerspective(45f, (float) this.cam.getWidth() / this.cam.getHeight(), 0.01f, 1000f);

		final ProgressDialog progressDialog = new ProgressDialog(null);
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					final ProgressMonitor mainProgressMonitor = new ProgressMonitor(progressDialog);

					final HashMap<String, PLYConfigTransformation> plyConfigTransformations = SimplePLYConfigParser.parse(StanfordBunny.class.getClassLoader().getResource("bun.conf").openStream(), new ProgressMonitor(mainProgressMonitor));

//					final String[] plyFiles = new String[] { "bun000.ply", "bun180.ply" }; /* Just 2 for testing. */
//					final String[] plyFiles = new String[] { "bun000.ply", "bun045.ply", "bun090.ply", "bun180.ply", "bun270.ply", "bun315.ply", "top2.ply", "top3.ply", "chin.ply", "ear_back.ply" };
					final String[] plyFiles = new String[] { "bun000.ply", "bun090.ply", "bun180.ply", "bun270.ply" };
//					final String[] plyFiles = new String[] { "bun000.ply", "bun090.ply", "bun180.ply", "bun270.ply", "bun045.ply" };

					final int plyFileCount = plyFiles.length;
					final float percentPerPart = 100.0f / plyFileCount;

					for(int i = 0; i < plyFileCount; i++) {
						final String plyFile = plyFiles[i];
						progressDialog.setLabelText("Parsing '" + plyFile + "' ...");

						/* Load PLY-File. */
						mainProgressMonitor.setSubMonitorRange((int) (percentPerPart * i), (int) (percentPerPart * (i + 1)));
						final float[] bunnyPartPointCloudVertexCoordinates = StanfordBunny.this.loadPLYFile(plyFile, new ProgressMonitor(mainProgressMonitor));

						/* Transform vertices as conf file described. */
						final PLYConfigTransformation plyConfigTransformation = plyConfigTransformations.get(plyFile);
						StanfordBunny.this.transformVertices(bunnyPartPointCloudVertexCoordinates, plyConfigTransformation.getTranslation(), plyConfigTransformation.getQuaternion());

						/* Create Geometry. */
						final Geometry bunnyPartPointCloudGeometry = new Geometry(plyFile, new PointCloud(bunnyPartPointCloudVertexCoordinates));
						bunnyPartPointCloudGeometry.setMaterial(StanfordBunny.this.getSolidColorMaterial(1.0f / plyFileCount * i));

						StanfordBunny.this.rootNode.attachChild(bunnyPartPointCloudGeometry);
					}

					progressDialog.dispose();
				} catch (final IOException e) {
					e.printStackTrace();
				}
			}
		}).start();

		progressDialog.setVisible(true);
	}

	@Override
	public void simpleUpdate(final float pSecondsElapsed) {
		this.mRotation += pSecondsElapsed;
		this.rootNode.setLocalRotation(this.rootNode.getLocalRotation().fromAngles(0, this.mRotation / FastMath.HALF_PI, 0));
	}

	@Override
	public void simpleRender(final RenderManager pRenderManager) {

	}

	// ===========================================================
	// Methods
	// ==========================================================

	private Material getSolidColorMaterial(final float pHue) {
		final Material solidColorMaterial = StanfordBunny.this.mSolidColorMaterial.clone();

		final Color color = new Color(Color.HSBtoRGB(pHue, 1.0f, 1.0f));
		solidColorMaterial.setColor("m_Color", new ColorRGBA(color.getRed() / 255.0f, color.getGreen() / 255.0f, color.getBlue() / 255.0f, 0.5f));

		return solidColorMaterial;
	}

	private float[] loadPLYFile(final String pPLYFile, final ProgressMonitor pProgressMonitor) throws IOException {
		return SimplePLYParser.parseAscii(StanfordBunny.class.getClassLoader().getResource(pPLYFile).openStream(), pProgressMonitor);
	}

	private void transformVertices(final float[] pPointCloudVertexCoordinates, final Vector3f pTranslation, final Quaternion pQuaternion) {
		final Vector3f current = new Vector3f();

		for(int i = 0; i < pPointCloudVertexCoordinates.length; i+=3) {
			current.set(pPointCloudVertexCoordinates[i], pPointCloudVertexCoordinates[i + 1], pPointCloudVertexCoordinates[i + 2]);

			current.addLocal(pTranslation);

			pQuaternion.mult(current, current);

			pPointCloudVertexCoordinates[i + 0] = current.x;
			pPointCloudVertexCoordinates[i + 1] = -current.y;
			pPointCloudVertexCoordinates[i + 2] = current.z;
		}
	}

	// ===========================================================
	// Inner and Anonymous Classes
	// ===========================================================
}
